home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / XMODEM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-30  |  11.2 KB  |  522 lines

  1. /*
  2.  * A version of Ward Christensen's file transfer protocol for
  3.  * Unix System V or 4.2 bsd.
  4.  *
  5.  *        Emmet P. Gray, ..!ihnp4!uiucuxc!fthood!egray, 16 Aug 85
  6.  *
  7.  * Modified by Sanford Zelkovitz   08/18/86
  8.  * Last modification date = 05/20/87
  9.  * Modified for KA9Q NOS BBS - WA3DSP 2/93
  10.  */
  11.  
  12.  
  13. #include "global.h"
  14. #include <sys/stat.h>
  15. #include <stdarg.h>
  16. #include "dirutil.h"
  17. #include "timer.h"
  18. #include "socket.h"
  19. #include "mailbox.h"
  20.  
  21. #if !defined(_lint)
  22. static char rcsid[] OPTIONAL = "$Id: xmodem.c,v 1.15 1997/07/31 00:44:20 root Exp root $";
  23. #endif
  24.  
  25. #if defined(TIPMAIL) && defined(XMODEM)
  26.  
  27. #ifdef min
  28. #undef min
  29. #endif
  30.  
  31.  
  32. #define SPEED 2400        /* Serial line Baudrate */
  33.  
  34. #define MAXERRORS 10        /* max number of times to retry */
  35. #define SECSIZE   128        /* CP/M sector, transmission block */
  36. #define CPMEOF    26        /* End Of File (for CP/M) */
  37. #ifndef SOH
  38. #define SOH       1        /* Start Of Header */
  39. #define STX       2        /* Start of 1K block */
  40. #define EOT       4        /* End Of Transmission */
  41. #endif
  42. #define ACK       6        /* ACKnowledge */
  43. #define NAK       21        /* Negative AcKnowledge */
  44. #define CAN       24        /* CANcel */
  45. #ifndef BS
  46. #define BS        8        /* Backspace */
  47. #endif
  48.  
  49.  
  50. static int xm_recvfile (char *, struct mbx * m);
  51. static int xm_sendfile (char *, struct mbx * m);
  52. static unsigned char getchar_t (int thesocket);
  53. static void update_crc (unsigned char c, unsigned char *crc1, unsigned char *crc2);
  54. static void error (struct mbx * m);
  55. static void print_text (struct mbx * m, const char *fmt,...);
  56. static void rawmode (struct mbx *);
  57. static void restoremode (struct mbx *);
  58.  
  59.  
  60.  
  61. int
  62. doxmodem (char mode, char *filename, void *p)
  63. {
  64. int exit_return = 0, oldflush;
  65. struct mbx *m;
  66.  
  67.     m = (struct mbx *) p;
  68.     oldflush = setflush (m->user, -1);
  69.  
  70.     switch (mode) {
  71.         case 'r':
  72.         case 'R':
  73.             exit_return = xm_recvfile (filename, m);
  74.             break;
  75.         case 's':
  76.         case 'S':
  77.             exit_return = xm_sendfile (filename, m);
  78.             break;
  79.         default:
  80.             print_text (m, "Xmodem: Invalid Option\n");
  81.     }
  82.     restoremode (m);
  83.     usputc (m->user, '\n');
  84.     usflush (m->user);
  85.     (void) setflush (m->user, oldflush);
  86.     return (exit_return);
  87. }
  88.  
  89.  
  90.  
  91. /* send a file to the remote */
  92. static int
  93. xm_sendfile (char *tfile, struct mbx *m)
  94. {
  95. FILE *fp;
  96. unsigned char chr, thechecksum, block, sector[SECSIZE];
  97. unsigned char crc1, crc2, mode, errcount, errcount2, two_can;
  98. int i, nbytes, speed = SPEED;
  99. long size, min, sec;
  100.  
  101.     if ((size = fsize (tfile)) == -1) {
  102.         print_text (m, "xmodem: Can't open '%s' for read\n", tfile);
  103.         return (1);
  104.     }
  105.     if ((fp = fopen (tfile, READ_BINARY)) == NULLFILE) {
  106.         print_text (m, "xmodem: Can't open '%s' for read\n", tfile);
  107.         return (1);
  108.     }
  109.     size = (size / 128L) + 1L;
  110.     sec = size * 128L * 15L / speed;
  111.     min = sec / 60L;
  112.     sec = sec - min * 60L;
  113.     print_text (m, "\nFile open: %d records\n", size);
  114.     print_text (m, "Send time: %ld min, %ld sec at %d baud\n", min, sec, speed);
  115.     print_text (m, "To cancel: use CTRL-X numerous times\n");
  116.     print_text (m, "Waiting receive ready signal\n");
  117.  
  118.     kpause (3000L);
  119.     rawmode (m);
  120.     errcount = 0;
  121.     mode = 0;
  122.     two_can = 0;
  123.     block = 1;
  124.  
  125.     while (errcount < MAXERRORS) {
  126.         chr = getchar_t (m->user);
  127.         if (chr == NAK)    /* checksum mode */
  128.             break;
  129.         if (chr == 'C') {    /* CRC mode */
  130.             mode = 1;
  131.             break;
  132.         }
  133.         if (chr == CAN) {
  134.             if (two_can) {
  135.                 kpause (3000L);
  136.                 print_text (m, "\nxmodem: Abort request received\n");
  137.                 (void) fclose (fp);
  138.                 return (1);
  139.             }
  140.             two_can = 1;
  141.         } else
  142.             two_can = 0;
  143.  
  144.         errcount++;
  145.     }
  146.     if (errcount >= MAXERRORS) {
  147.         kpause (3000L);
  148.         print_text (m, "xmodem: Timed out on acknowledge\n");
  149.         (void) fclose (fp);
  150.         return (1);
  151.     }
  152.     two_can = 0;
  153.     while ((nbytes = (int) fread (sector, 1, 128, fp)) != 0) {
  154.         if (nbytes < SECSIZE) {    /* fill short sector */
  155.             for (i = nbytes; i < SECSIZE; i++)
  156.                 sector[i] = CPMEOF;
  157.         }
  158.         errcount = 0;
  159.         while (errcount < MAXERRORS) {
  160.             usputc (m->user, SOH);    /* the header */
  161.             usputc (m->user, block);    /* the block number */
  162.             chr = ~block;
  163.             usputc (m->user, chr);    /* it's complement */
  164.             thechecksum = 0;
  165.             crc1 = 0;
  166.             crc2 = 0;
  167.             for (i = 0; i < SECSIZE; i++) {
  168.                 usputc (m->user, sector[i]);
  169.                 if (mode)
  170.                     update_crc (sector[i], &crc1, &crc2);
  171.                 else
  172.                     thechecksum += sector[i];
  173.             }
  174.             if (mode) {
  175.                 update_crc (0, &crc1, &crc2);
  176.                 update_crc (0, &crc1, &crc2);
  177.                 usputc (m->user, crc1);
  178.                 usputc (m->user, crc2);
  179.  
  180.             } else
  181.                 usputc (m->user, thechecksum);
  182.  
  183.             usflush (m->user);
  184.             errcount2 = 0;
  185. rec_loop:
  186.  
  187.             chr = getchar_t (m->user);
  188.             if (chr == CAN) {
  189.                 if (two_can) {
  190.                     kpause (3000L);
  191.                     print_text (m, "\nxmodem: Abort request received\n");
  192.                     (void) fclose (fp);
  193.                     return (1);
  194.                 }
  195.                 two_can = 1;
  196.             } else
  197.                 two_can = 0;
  198.  
  199.             if (chr == ACK)
  200.                 break;    /* got it! */
  201.             /* noise on line? */
  202.             if (chr != NAK) {
  203.                 ++errcount2;
  204.                 if (errcount2 >= MAXERRORS) {
  205.                     error (m);
  206.                     (void) fclose (fp);
  207.                     return 1;
  208.                 }
  209.                 goto rec_loop;
  210.             }
  211.             errcount++;
  212.         }
  213.         if (errcount >= MAXERRORS) {
  214.             error (m);
  215.             (void) fclose (fp);
  216.             return (1);
  217.         }
  218.         block++;
  219.     }
  220.     errcount = 0;
  221.     while (errcount < MAXERRORS) {
  222.         usputc (m->user, EOT);
  223.         usflush (m->user);
  224.         if (getchar_t (m->user) == ACK) {
  225.             (void) fclose (fp);
  226.             kpause (6000L);
  227.             log (m->user, "Xmodem: Download - %s", tfile);
  228.             print_text (m, "Xmodem: File sent OK\n");
  229.             return (0);
  230.         }
  231.         errcount++;
  232.     }
  233.     (void) fclose (fp);
  234.     kpause (3000L);
  235.     error (m);
  236.     return (1);
  237. }
  238.  
  239.  
  240.  
  241. /* receive a file from the remote */
  242. static int
  243. xm_recvfile (char *tfile, struct mbx *m)
  244. {
  245. FILE *fp;
  246. unsigned char hdr, blk, cblk, tmp, thecksum = 0, crc1, crc2;
  247. unsigned char c1 = 0, c2 = 0, sum, block, sector[SECSIZE];
  248. unsigned char first, mode = 0, errcount, two_can;
  249. int i;
  250.  
  251.     if (!access (tfile, 00)) {
  252.         print_text (m, "xmodem: File %s already exists\n", tfile);
  253.         return (1);
  254.     }
  255.     if ((fp = fopen (tfile, WRITE_BINARY)) == NULLFILE) {
  256.         print_text (m, "xmodem: Can't open '%s' for write\n", tfile);
  257.         return (1);
  258.     }
  259.     print_text (m, "File open - ready to receive\n");
  260.     print_text (m, "To cancel: use CTRL-X numerous times\n");
  261.     kpause (3000L);
  262.     rawmode (m);
  263.     errcount = 0;
  264.     block = 1;
  265.     first = 0;
  266.     two_can = 0;
  267.  
  268.     kpause (3000L);
  269.     while (errcount < MAXERRORS) {
  270.         if (errcount < (MAXERRORS / 2)) {
  271.             usputc (m->user, 'C');    /* try CRC mode first */
  272.             usflush (m->user);
  273.             mode = 1;
  274.         } else {
  275.             usputc (m->user, NAK);    /* then checksum */
  276.             usflush (m->user);
  277.             mode = 0;
  278.         }
  279.         if ((hdr = getchar_t (m->user)) == SOH) {
  280.             first = 1;
  281.             break;
  282.         }
  283.         if (hdr == CAN) {
  284.             if (two_can) {
  285.                 kpause (3000L);
  286.                 print_text (m, "\nxmodem: Abort request received\n");
  287.                 (void) fclose (fp);
  288.                 unlink (tfile);
  289.                 return (1);
  290.             }
  291.             two_can = 1;
  292.         } else
  293.             two_can = 0;
  294.         errcount++;
  295.     }
  296.     if (errcount >= MAXERRORS) {
  297.         kpause (3000L);
  298.         print_text (m, "\nxmodem: Timed out on acknowledge\n");
  299.         (void) fclose (fp);
  300.         unlink (tfile);
  301.         return (1);
  302.     }
  303.     errcount = 0;
  304.     two_can = 0;
  305.     while (errcount < MAXERRORS) {
  306.  
  307.         if (first) {
  308.             hdr = SOH;
  309.             first = 0;
  310.         } else
  311.             hdr = getchar_t (m->user);
  312.  
  313.         if (hdr == CAN) {
  314.             if (two_can) {
  315.                 kpause (3000L);
  316.                 print_text (m, "\nxmodem: Abort request received\n");
  317.                 (void) fclose (fp);
  318.                 unlink (tfile);
  319.                 return (1);
  320.             }
  321.             two_can = 1;
  322.             continue;
  323.         } else
  324.             two_can = 0;
  325.  
  326.         if (hdr == EOT)    /* done! */
  327.             break;
  328.  
  329.         if (hdr != SOH) {    /* read in junk for 6 seconds */
  330.             kalarm (6000L);
  331.             while (errno != EALARM)
  332.                 (void) rrecvchar (m->user);
  333.             kalarm (0L);    /* cancel alarm */
  334.             first = 0;
  335.             goto nak;
  336.         }
  337.         blk = getchar_t (m->user);
  338.         cblk = getchar_t (m->user);
  339.         crc1 = 0;
  340.         crc2 = 0;
  341.         sum = 0;
  342.         for (i = 0; i < SECSIZE; i++) {
  343.             sector[i] = getchar_t (m->user);
  344.             if (mode)
  345.                 update_crc (sector[i], &crc1, &crc2);
  346.             else
  347.                 sum += sector[i];
  348.         }
  349.         if (mode) {
  350.             c1 = getchar_t (m->user);
  351.             c2 = getchar_t (m->user);
  352.         } else
  353.             thecksum = getchar_t (m->user);
  354.         if (blk != block && blk != (block - 1))
  355.             goto nak;
  356.         tmp = ~blk;
  357.         if (cblk != tmp)
  358.             goto nak;
  359.         if (mode) {
  360.             update_crc (0, &crc1, &crc2);
  361.             update_crc (0, &crc1, &crc2);
  362.             if (c1 != crc1 || c2 != crc2)
  363.                 goto nak;
  364.         } else {
  365.             if (thecksum != sum)
  366.                 goto nak;
  367.         }
  368.         if (block == blk) {
  369.             (void) fflush (fp);
  370.             if (fwrite (sector, sizeof (sector[0]), SECSIZE, fp) != SECSIZE) {
  371.                 error (m);
  372.                 print_text (m, "         File write error - Partial file deleted\n");
  373.                 (void) fclose (fp);
  374.                 unlink (tfile);
  375.                 return (1);
  376.             }
  377.         }
  378.         block = blk + 1;
  379.         usputc (m->user, ACK);    /* got it! */
  380.         usflush (m->user);
  381.         errcount = 0;
  382.         continue;
  383.  
  384. nak:
  385.         usputc (m->user, NAK);
  386.         /* do it over */
  387.         usflush (m->user);
  388.         errcount++;
  389.     }
  390.     if (errcount == MAXERRORS) {
  391.         error (m);
  392.         (void) fclose (fp);
  393.         unlink (tfile);
  394.         return (1);
  395.     }
  396.     usputc (m->user, ACK);
  397.     usflush (m->user);
  398.     kpause (3000L);
  399.     (void) fclose (fp);
  400.     log (m->user, "Xmodem: Upload - %s", tfile);
  401.     print_text (m, "Xmodem: File received OK\n");
  402.     return (0);
  403. }
  404.  
  405.  
  406.  
  407. /* exceeded the maximum number of retry's */
  408. static void
  409. error (struct mbx *m)
  410. {
  411. int i;
  412.  
  413.     for (i = 0; i < 9; i++)
  414.         usputc (m->user, CAN);
  415.  
  416.     usflush (m->user);
  417.     kpause (1000L);
  418.     for (i = 0; i < 9; i++)
  419.         usputc (m->user, BS);
  420.  
  421.     usflush (m->user);
  422.     kpause (3000L);
  423.     print_text (m, "\nxmodem: Exceeded error limit...aborting\n");
  424.     return;
  425. }
  426.  
  427.  
  428.  
  429. /* update the CRC bytes */
  430. static void
  431. update_crc (unsigned char c, unsigned char *crc1, unsigned char *crc2)
  432. {
  433. register int i, temp;
  434. register unsigned char carry, c_crc1, c_crc2;
  435.  
  436.     for (i = 0; i < 8; i++) {
  437.         temp = c * 2;
  438.         c = uchar(temp);    /* rotate left */
  439.         carry = ((temp > 255) ? 1 : 0);
  440.         temp = *crc2 * 2;
  441.         *crc2 = uchar(temp);
  442.         *crc2 |= carry;    /* rotate with carry */
  443.         c_crc2 = ((temp > 255) ? 1 : 0);
  444.         temp = *crc1 * 2;
  445.         *crc1 = uchar(temp);
  446.         *crc1 |= c_crc2;
  447.         c_crc1 = ((temp > 255) ? 1 : 0);
  448.         if (c_crc1) {
  449.             *crc2 ^= 0x21;
  450.             *crc1 ^= 0x10;
  451.         }
  452.     }
  453.     return;
  454. }
  455.  
  456.  
  457.  
  458. /* getchar with a 5 sec time out */
  459. static unsigned char
  460. getchar_t (int s)
  461. {
  462. unsigned char c;
  463.  
  464.     /* only have 5 sec... */
  465.     kalarm (5000L);
  466.     /* Wait for something to happen */
  467.     c = uchar(rrecvchar (s));
  468.     kalarm (0L);
  469.     return (c);
  470. }
  471.  
  472.  
  473.  
  474. /* put the stdin/stdout in the "raw" mode */
  475. static void
  476. rawmode (struct mbx *m)
  477. {
  478.     (void) seteol (m->user, 0);
  479.     (void) seteol (m->tip->s, 0);
  480.     (void) sockmode (m->tip->s, SOCK_BINARY);
  481.     (void) sockmode (m->user, SOCK_BINARY);
  482.     m->tip->raw = 1;
  483. }
  484.  
  485.  
  486.  
  487. static void
  488. restoremode (struct mbx *m)
  489. {
  490.     while (socklen (m->user, 0) != 0)
  491.         (void) recv_mbuf (m->user, NULL, 0, NULLCHAR, 0);
  492.     (void) seteol (m->user, "\n");
  493.     (void) seteol (m->tip->s, "\n");
  494.     (void) sockmode (m->user, SOCK_ASCII);
  495.     (void) sockmode (m->tip->s, SOCK_ASCII);
  496.     m->tip->raw = 0;
  497. }
  498.  
  499.  
  500.  
  501. void
  502. #ifdef TNOS_68K
  503. print_text (m, fmt)
  504. struct mbx *m;
  505. const char *fmt;
  506. #else
  507. print_text (struct mbx *m, const char *fmt,...)
  508. #endif
  509. {
  510. va_list ap;
  511. char buf[80];
  512.  
  513.     restoremode (m);
  514.     va_start (ap, fmt);        /*lint !e718 !e746 */
  515.     (void) vsprintf (buf, fmt, ap);
  516.     va_end (ap);
  517.     usprintf (m->user, buf);
  518.     usflush (m->user);
  519. }
  520.  
  521. #endif
  522.